package utils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.ArrayList;

public class SHA1 {

    /**
     * Computing hash value with SHA1 algo.
     *
     * @param is Input Stream
     * @return byte SHA1 value
     * @throws Exception message digest exception
     */
    public static byte[] SHA1Checksum(InputStream is) throws Exception {
        byte[] buffer = new byte[1024];
        MessageDigest complete = MessageDigest.getInstance("SHA-1");
        int numRead = 0;
        while (numRead != -1) {
            numRead = is.read(buffer);
            if (numRead > 0) {
                complete.update(buffer, 0, numRead);
            }
        }
        is.close();
        return complete.digest();
    }

    /**
     * Converting hash value to String.
     *
     * @param sha1 SHA1 byte value
     * @return String SHA1 value
     */
    public static String getHash(byte[] sha1) {
        StringBuilder hashValue = new StringBuilder();
        for (byte b : sha1) {
            hashValue.append(Integer.toString((b >> 4) & 0x0F, 16)).append(Integer.toString(b & 0x0F, 16));
        }
        return hashValue.toString();
    }

    /**
     * Getting hash value of a file.
     *
     * @param file File need to be computed
     * @return SHA1
     * @throws Exception message digest exception
     */
    public static String getHash(File file) throws Exception {
        if(file.isFile()) {
            byte[] sha1 = SHA1Checksum(new FileInputStream(file));
            return getHash(sha1);
        }
        else if(file.isDirectory()){
            ArrayList<File> files = new ArrayList<File>();
            StringBuilder timeStamp = new StringBuilder();
            for( File f : file.listFiles())
                timeStamp.append(f.lastModified());

            return SHA1.getHash(timeStamp.toString());
        }
        return null;
    }

    /**
     * Getting hash value of a String.
     *
     * @param value String
     * @return String SHA1
     * @throws Exception message digest exception
     */
    public static String getHash(String value) throws Exception {
        byte[] sha1 = SHA1Checksum(new ByteArrayInputStream(value.getBytes()));
        return getHash(sha1);
    }

    /**
     * Getting hash for directory
     *
     * @param files file list
     * @return MessageDigest
     */
    public static String getHash(ArrayList<File> files) {

        try {
            MessageDigest hash = MessageDigest.getInstance("SHA-1");
            //Size of buffer
            int bufferSize = 256 * 1024;

            //Using lambda to sort the files by the size of files
            files.sort((o1, o2) -> {
                if (o2.length() - o1.length() > 0) return 1;
                else if (o2.length() == o1.length()) return 0;
                else return -1;
            });

            //Computing hash
            for (File file : files) {
                FileInputStream fs = new FileInputStream(file);
                DigestInputStream ds = new DigestInputStream(fs, hash);

                byte[] buffer = new byte[bufferSize];

                while (ds.read(buffer) > 0) ;
                hash = ds.getMessageDigest();

                ds.close();

            }
            BigInteger bigInt = new BigInteger(1, hash.digest());

            return bigInt.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }


}
